﻿using NIDViewer.Enums;
using NIDViewer.Model;
using NumSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NIDViewer.FlattenImage
{
    public class ImageProcessor
    {
        /// <summary>
        /// 一阶拟合
        /// 图像进行​​逐行平面拟合​​，通过移除每行的线性趋势来实现图像平坦化
        /// </summary>
        /// <param name="image"></param>
        /// <param name="mask"></param>
        /// <returns></returns>
        public static Tuple<NDArray, NDArray> FlattenPerLine1(NDArray image, NDArray? mask)
        {
            int n = image.shape[0]; // 行数
            int m = image.shape[1]; // 列数
            NDArray imageFlat = np.zeros(image.shape); // 平坦化后的图像
            NDArray imagePlane = np.zeros(image.shape); // 拟合的平面（每行是一个线性平面）

            for (int i = 0; i < n; i++)
            {
                NDArray lineImage = image[i, ":"]; // 获取当前行

                // 计算掩码
                bool[] lineMask = mask?[i, null].GetData<double>().Select(x => x != 0).ToArray();// 确保掩码是布尔类型

                // 拟合线
                double[] coefficients = FitLine(lineImage, lineMask); // 拟合函数
                for (int j = 0; j < m; j++)
                {
                    imagePlane[i, j] = coefficients[0] + coefficients[1] * j; // y = mx + b
                    imageFlat[i, j] = image[i, j] - imagePlane[i, j]; // 从原始图像减去拟合平面
                }
            }
            return new Tuple<NDArray, NDArray>(imageFlat, imagePlane);
        }

        /// <summary>
        /// 最小二乘拟合
        /// </summary>
        /// <param name="data"></param>
        /// <param name="mask"></param>
        /// <returns></returns>
        private static double[] FitLine(NDArray data, bool[] mask)
        {
            double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
            int count = 0;
            int length = data.shape[0];

            for (int i = 0; i < length; i++)
            {
                if (mask == null || mask[i])
                {
                    sumX += i;
                    sumY += data[i];
                    sumXY += i * data[i];
                    sumX2 += i * i;
                    count++;
                }
            }
            double slope = (count * sumXY - sumX * sumY) / (count * sumX2 - sumX * sumX);
            double intercept = (sumY - slope * sumX) / count;
            return new double[] { intercept, slope };
        }
    }
}
